package com.xinghanlaser.applet.base.auth.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinghanlaser.applet.base.auth.entity.RoleDO;
import com.xinghanlaser.applet.base.auth.entity.RoleResourceDO;
import com.xinghanlaser.applet.base.auth.entity.UserRoleDO;
import com.xinghanlaser.applet.base.auth.mapper.RoleMapper;
import com.xinghanlaser.applet.base.auth.query.RoleQuery;
import com.xinghanlaser.applet.base.auth.service.IResourceService;
import com.xinghanlaser.applet.base.auth.service.IRoleResourceService;
import com.xinghanlaser.applet.base.auth.service.IRoleService;

import com.xinghanlaser.applet.base.auth.service.IUserRoleService;
import com.xinghanlaser.applet.base.auth.vo.RoleDataVO;
import com.xinghanlaser.applet.base.auth.vo.RolePostVO;
import com.xinghanlaser.applet.base.auth.vo.RolePutVO;
import com.xinghanlaser.applet.base.common.entity.ResponseEntity;
import com.xinghanlaser.applet.base.common.exception.ServiceException;
import com.xinghanlaser.applet.base.common.utils.QueryUtil;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.xinghanlaser.applet.base.auth.constant.AdminConstant.SUPER_ADMIN_ROLE_ID;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author dym
 * @since 2019-11-19
 */
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, RoleDO> implements IRoleService {

    @Resource
    private RoleMapper roleMapper;

    @Resource
    private IResourceService resourceService;

    @Resource
    private IRoleResourceService roleResourceService;

    @Resource
    private IUserRoleService userRoleService;

    @Override
    public Set<Long> getExistRoleList(Collection<Long> ids) {
        Map<Long, RoleDO> roleMap = this.list(Wrappers.lambdaQuery(new RoleDO()).select(RoleDO::getId))
                .stream()
                .collect(Collectors.toMap(RoleDO::getId, item -> item));

        if(roleMap.isEmpty()) return new HashSet<>();

        return ids.stream()
                .filter(roleMap::containsKey)
                .collect(Collectors.toSet());
    }

    @Override
    public ResponseEntity<ResponseEntity.PageResult<RoleDataVO>> pageByCondition(RoleQuery query) {
        ResponseEntity.PageResult<RoleDataVO> result = QueryUtil.query(
                query,

                () -> roleMapper.pageByCondition(query),

                voList -> {
                    // 设置管理员信息
                    for (RoleDataVO roleDataVO : voList) {
                        roleDataVO.setIsSuperAdmin(SUPER_ADMIN_ROLE_ID.equals(roleDataVO.getId()));
                    }
                    return voList;
                }
        );

        return ResponseEntity.okPage(result);
    }

    @Override
    public ResponseEntity<RoleDataVO> getRoleById(Long id) {
        RoleDO roleDO = validatedRoleId(id);

        RoleDataVO vo = BeanUtil.copyProperties(roleDO, RoleDataVO.class);

        return ResponseEntity.ok(vo);
    }

    @Override
    public ResponseEntity<Set<RoleDataVO>> getRoleListByUserId(Long id) {
        Set<RoleDataVO> result = roleMapper.selectRoleByUserId(id)
                .stream()
                .map(item -> BeanUtil.copyProperties(item, RoleDataVO.class))
                .collect(Collectors.toSet());

        return ResponseEntity.ok(result);
    }

    @Override
    @DSTransactional
    public ResponseEntity<Void> addRole(RolePostVO postVO) {
        // 校验角色代码是否重复
        validateRoleCode(postVO.getRoleCode());

        // 保存角色信息
        RoleDO entity = BeanUtil.copyProperties(postVO, RoleDO.class);
        this.save(entity);

        // 处理权限信息
        final Set<Long> resourceIds = postVO.getResourceIds();
        final Long roleId = entity.getId();
        if(CollectionUtil.isEmpty(resourceIds)) return ResponseEntity.okMsg("新增角色成功");

        Optional.of(
            resourceService.getExistResourceList(resourceIds)
                .stream()
                .map(resourceId -> RoleResourceDO.builder()
                        .roleId(roleId)
                        .resourceId(resourceId)
                        .build()
                )
                .collect(Collectors.toSet())
        )
        .filter(CollectionUtil::isNotEmpty)
        .ifPresent(roleResourceService::saveBatch);

        return ResponseEntity.okMsg("新增角色成功");
    }

    @Override
    @DSTransactional
    public ResponseEntity<Void> modRole(RolePutVO putVO) {
        final Long roleId = putVO.getId();

        // 校验角色ID是否存在
        validatedRoleId(roleId);

        // 更新角色数据
        RoleDO entity = BeanUtil.copyProperties(putVO, RoleDO.class);
        this.updateById(entity);

        final Set<Long> resourceIds = putVO.getResourceIds();
        if(CollectionUtil.isEmpty(resourceIds)){
            roleResourceService.remove(Wrappers.lambdaQuery(new RoleResourceDO()).eq(RoleResourceDO::getRoleId, roleId));
            return ResponseEntity.okMsg("修改角色信息成功");
        }
        Optional.of(
            resourceService.getExistResourceList(resourceIds)
                .stream()
                .map(resourceId -> RoleResourceDO.builder()
                        .roleId(roleId)
                        .resourceId(resourceId)
                        .build()
                )
                .collect(Collectors.toSet())
        )
        .filter(CollectionUtil::isNotEmpty)
        .ifPresent(roleResourceService::saveBatch);

        return ResponseEntity.okMsg("修改角色信息成功");
    }

    @Override
    @DSTransactional
    public ResponseEntity<Void> delRole(Long id) {
        validatedRoleId(id);

        // 删除角色信息
        this.removeById(id);

        // 删除角色与用户的关联
        userRoleService.remove(Wrappers.lambdaQuery(new UserRoleDO()).eq(UserRoleDO::getRoleId, id));

        // 删除角色与资源的关联
        roleResourceService.remove(Wrappers.lambdaQuery(new RoleResourceDO()).eq(RoleResourceDO::getRoleId, id));

        return ResponseEntity.okMsg("删除角色成功");
    }

    private RoleDO validatedRoleId(Long roleId){
        return Optional.ofNullable(this.getById(roleId))
                .orElseThrow(() -> new ServiceException("角色ID不存在"));
    }

    private void validateRoleCode(String roleCode){
        if(StrUtil.isBlank(roleCode)){
            throw new ServiceException("校验角色代码不能为空");
        }

        RoleDO exist = this.getOne(Wrappers.lambdaQuery(new RoleDO())
                .select(RoleDO::getId)
                .eq(RoleDO::getRoleCode, roleCode)
                .last("LIMIT 1")
        );

        if(exist != null){
            throw new ServiceException("角色代码已存在");
        }
    }
}
